home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / bookmark_previews-0.6.5-fx.xpi / chrome / content / previewService.js < prev    next >
Text File  |  2008-05-27  |  32KB  |  802 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is the Bookmark Previews extension.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * John Marshall <JohnM555@gmail.com>.
  18.  * Portions created by the Initial Developer are Copyright (C) 2007
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   John Marshall <JohnM555@gmail.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37. if (!("STATE_START" in window)){
  38.   STATE_START = Components.interfaces.nsIWebProgressListener.STATE_START;
  39.   STATE_STOP = Components.interfaces.nsIWebProgressListener.STATE_STOP;
  40. }
  41. var PreviewService = {
  42.   PREVIEW_WIDTH: 600,
  43.   timeouts: [],
  44.   listener : null,
  45.   bookmarks: null,
  46.   generator : null,
  47.   _prefs : null,
  48.   get prefs(){
  49.     if (!this._prefs){
  50.       this._prefs = Components.classes["@mozilla.org/preferences-service;1"].
  51.                     getService(Components.interfaces.nsIPrefService);
  52.       this._prefs = this._prefs.getBranch("extensions.bookmarkpreviews.");
  53.     }
  54.     return this._prefs;
  55.   },
  56.   isFF3 : false,
  57.   /* since load is called around 7 times for some sites before it actually loads,
  58.     just cache the last isBookmark check. isBookmark might not take long, but whatever */
  59.   _lastCheck : {uri : {spec : null}, isBookmarked : false},
  60.   onLoad: function() {
  61.     var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
  62.                         .getService(Components.interfaces.nsIXULAppInfo);
  63.     var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
  64.                                    .getService(Components.interfaces.nsIVersionComparator);
  65.     PreviewService.isFF3 = (versionChecker.compare(appInfo.version, "3.0a") >= 0);
  66.     if (PreviewService.isFF3)
  67.       PreviewService.bookmarks = new PreviewServiceFF3();
  68.     else{
  69.       /*
  70.         Use the listener to check for redirected bookmarks.
  71.         FF3 doesn't need this because it has a better bookmarking system.
  72.       */
  73.       PreviewService.bookmarks = new PreviewServiceFF2();
  74.       PreviewService.listener = new BookmarkPreviewsProgressListener();
  75.       PreviewService.listener.onURILoad = PreviewService.onURILoad;
  76.       PreviewService.listener.getBrowser = PreviewService.getBrowserForURI;
  77.       gBrowser.addProgressListener(PreviewService.listener,Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
  78.     }
  79.     gBrowser.addEventListener('load',PreviewService.onPageLoad,true);
  80.     var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
  81.                                .getService(Components.interfaces.nsIVersionComparator);
  82.     var em = Components.classes["@mozilla.org/extensions/manager;1"]
  83.                    .getService(Components.interfaces.nsIExtensionManager);
  84.     var addon = em.getItemForID("bookmarkpreviews@mozdev.org");
  85.     var currentVersion = addon.version;
  86.     var lastVersion = PreviewService.prefs.getCharPref("currentVersion");
  87.     if (lastVersion == "none") {
  88.       PreviewService.prefs.setCharPref("currentVersion", currentVersion);
  89.       PreviewService.showFirstRunDialog();
  90.     } else if (versionChecker.compare(currentVersion,lastVersion) > 0) {
  91.       PreviewService.prefs.setCharPref("currentVersion", currentVersion);
  92.       /* any upgrade code goes here */
  93.     }
  94.   },
  95.   onUnload: function() {
  96.     gBrowser.removeEventListener('load',PreviewService.onPageLoad,true);
  97.     if (!PreviewService.isFF3)
  98.       gBrowser.removeProgressListener(PreviewService.listener);
  99.     PreviewService.bookmarks.unload();
  100.   },
  101.   onPageLoad : function(event){
  102.     var doc = event.originalTarget;
  103.     if (doc instanceof Components.interfaces.nsIDOMDocument) {
  104.       if (doc.defaultView.frameElement) {
  105.         // Frame within a tab was loaded. doc should be the root document of
  106.         // the frameset.
  107.         // Find the root document:
  108.         while (doc.defaultView.frameElement) {
  109.           doc=doc.defaultView.frameElement.ownerDocument;
  110.         }
  111.       }
  112.       if (doc && doc.URL){
  113.         var uri = null;
  114.         try{/* catch malformed uri errors */
  115.           uri = makeURI(doc.URL);
  116.         }catch(e){}
  117.         if (!uri) return;
  118.         var browser = gBrowser.getBrowserForDocument(doc);
  119.         if (browser)
  120.           PreviewService.onURILoad(uri, browser);
  121.       }
  122.     }
  123.   },
  124.   onURILoad : function(uri,browser){
  125.     var isBookmarked = uri.equals(PreviewService._lastCheck.uri)?PreviewService._lastCheck.isBookmarked:PreviewService.bookmarks.isBookmarked(uri);
  126.     //dump("onuriload: "+isBookmarked+"\n");
  127.     if (isBookmarked && PreviewService.shouldCreatePreview(uri)){
  128.       if (PreviewService.timeouts[uri.spec]!=null)
  129.         clearTimeout(PreviewService.timeouts[uri.spec]);
  130.       /* Just to give it time to load a little more... */
  131.       PreviewService.timeouts[uri.spec]=setTimeout(function(){
  132.         PreviewService.timeouts[uri.spec]=null;
  133.         PreviewService.createPreviewFromBrowser(browser,uri);
  134.       },1500);
  135.     }
  136.     PreviewService._lastCheck.uri = uri;
  137.     PreviewService._lastCheck.isBookmarked = isBookmarked;
  138.   },
  139.   shouldCreatePreview : function(uri){
  140.     if (!uri || !uri.scheme) return false;
  141.     var allowHTTPS = this.prefs.getBoolPref("createForHTTPS");
  142.     var schemes = ["http","chrome"];
  143.     if (allowHTTPS) schemes.push("https");
  144.     return schemes.indexOf(uri.scheme)>-1;
  145.   },
  146.   getBrowserForURI : function(uri){
  147.     var numTabs = gBrowser.tabContainer.childNodes.length;
  148.     for(var index=0; index<numTabs; index++) {
  149.       var currentBrowser = gBrowser.getBrowserAtIndex(index);
  150.       if (uri.equals(currentBrowser.currentURI)) {
  151.         return currentBrowser;
  152.       }
  153.     }
  154.     return null;
  155.   },
  156.   bookmarkAdded : function(uri){
  157.     var currentBrowser = this.getBrowserForURI(uri);
  158.     if (currentBrowser)
  159.       this.createPreviewFromBrowser(currentBrowser);
  160.   },
  161.   createPreviewFromBrowser : function(browser, uri, aWidth, aHeight){
  162.     uri = uri ? uri : browser.currentURI;
  163.     //dump("creatpreviewfrombrowser: "+uri.spec+"\n");
  164.     var win=browser.contentWindow;
  165.     if (!win)/*the window may have closed before the timeout*/
  166.       return;
  167.     aWidth = (aWidth && aWidth!=0)?aWidth:win.innerWidth;
  168.     aHeight = (aHeight && aHeight!=0)?aHeight:win.innerHeight;
  169.     var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
  170.     var scale=this.PREVIEW_WIDTH/(aWidth);
  171.     canvas.height = Math.floor(scale*(aHeight));
  172.     canvas.width = this.PREVIEW_WIDTH;
  173.     var ctx = canvas.getContext("2d");
  174.     ctx.save();
  175.     ctx.scale(scale, scale);
  176.     ctx.drawWindow(win, 0, 0, aWidth, aHeight,"rgb(255,255,255)");
  177.     ctx.restore();
  178.     var data = canvas.toDataURL("image/jpeg", "");
  179.     if(data && data != "") {
  180.       try {
  181.         this.bookmarks.savePreview(data,uri);
  182.         this.sendUpdateNotification(uri);
  183.       } catch(e) {
  184.         Components.utils.reportError("Bookmarks Previews: Error saving bookmark image for uri\n"+uri.spec+"\nError: "+e+"\n");
  185.       }
  186.     }
  187.   },
  188.   sendUpdateNotification : function(uri){
  189.     /* When notifying the observers right away, the observers still seem to think
  190.       that the file doesn't exist. */
  191.     //dump("send update notification\n");
  192.     setTimeout(function(){
  193.       Components.classes["@mozilla.org/observer-service;1"]
  194.            .getService(Components.interfaces.nsIObserverService)
  195.            .notifyObservers(uri, "bookmarkpreviews:preview-updated", null);
  196.     },0);
  197.   },
  198.   showFirstRunDialog : function(){
  199.     var self = this;
  200.     var strbundleService = Components.classes ["@mozilla.org/intl/stringbundle;1"]
  201.                         .getService(Components.interfaces.nsIStringBundleService);
  202.     this.bundle = strbundleService.createBundle("chrome://bookmarkpreviews/locale/strings.properties");
  203.     var dialogQ = this.bundle.GetStringFromName("firstrundialog");
  204.     var dialogTitle = this.bundle.GetStringFromName("firstrundialogtitle");
  205.     /* Give it some time for the browser to actually load */
  206.     setTimeout(function(){
  207.       var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  208.                         .getService(Components.interfaces.nsIPromptService);
  209.       var result = prompts.confirm(window, dialogTitle, dialogQ);
  210.       if (result){
  211.         self.generateAllPreviews();
  212.       } else
  213.         delete this.bundle;
  214.     },2000);
  215.   },
  216.   generateAllPreviews : function(){
  217.     if (!this.generator || this.generator.status == "complete"){
  218.       this.generator = new PreviewGenerator();
  219.       this.generator.generateAllPreviews();
  220.     } else {
  221.       /* still generating */
  222.     }
  223.   }
  224. }
  225. function PreviewGenerator(){}
  226. PreviewGenerator.prototype = {
  227.   LOAD_TIMEOUT : 1000 * 60,
  228.   status: null,
  229.   generateAllPreviews : function(){
  230.     this.allBookmarks = PreviewService.bookmarks.getAllBookmarks();
  231.     if (!this.allBookmarks || this.allBookmarks.length==0){
  232.       this.allBookmarks = null;
  233.       return;
  234.     }
  235.     if (!this.bundle){
  236.       var strbundleService = Components.classes ["@mozilla.org/intl/stringbundle;1"]
  237.                         .getService(Components.interfaces.nsIStringBundleService);
  238.       this.bundle = strbundleService.createBundle("chrome://bookmarkpreviews/locale/strings.properties");
  239.     }
  240.     this.status = "loading";
  241.     this.curPreview = -1;
  242.     this.browser = document.getElementById("bookmarkpreviews-browser");
  243.     if (!this.browser){
  244.       this.browser = document.createElement("browser");
  245.       this.browser.setAttribute("id","bookmarkpreviews-browser");
  246.       this.browser.setAttribute("width",1);
  247.       this.browser.setAttribute("height",1);
  248.       this.browser.setAttribute("type","content");
  249.       this.browser = document.documentElement.appendChild(this.browser);
  250.     }
  251.     this.browser.hidden = false;
  252.  
  253.     /* The canvas can't save flash/java so don't waste time loading it*/
  254.     this.browser.docShell.allowPlugins = false;
  255.     /* So it doesn't launch any windows as its working in the background*/
  256.     this.browser.docShell.allowJavascript = false;
  257.     /* don't throw up password dialogs */
  258.     this.browser.docShell.allowAuth = false;
  259.     this.browser.stop();
  260.  
  261.     var listener = new BookmarkPreviewsProgressListener();
  262.     listener.getBrowser = function BP_GetBrowser(uri){
  263.       return document.getElementById("bookmarkpreviews-browser");
  264.     }
  265.     listener.onURILoad = this.onPreviewLoad;
  266.     listener.surpressDialogs = !PreviewService.isFF3;
  267.     this.browser.addProgressListener(listener,Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
  268.     this.previewGenListener = listener;
  269.     this.browser.addEventListener("load",this,true);
  270.     this.loadNextBookmark();
  271.   },
  272.   onPreviewLoad : function(uri){
  273.     var self = PreviewService.generator;
  274.     if (self.status == "loading" && uri.equals(self.allBookmarks[self.curPreview])){
  275.       if (self.generateTimeout!=null){
  276.         clearTimeout(self.generateTimeout);
  277.       }
  278.       self.generateTimeout=setTimeout(function(){
  279.         try{
  280.           if (self.status == "loading")
  281.             self.generatePreview(uri,self.browser);
  282.         }catch(e){
  283.           dump("previewload: error: "+e);
  284.         }
  285.       },1500);
  286.     }
  287.   },
  288.   generatePreview : function(uri){
  289.     this.generateTimeout = null;
  290.     var browser = this.browser;
  291.     if (!browser || !browser.stop){
  292.       return;
  293.     }
  294.     if (browser){
  295.       browser.stop();
  296.       PreviewService.createPreviewFromBrowser(browser,uri,gBrowser.boxObject.width,gBrowser.boxObject.height);
  297.     }
  298.     this.loadNextBookmark();
  299.   },
  300.   handleEvent : function(event){
  301.     if (event.type == "load")
  302.       this.onPageLoad(event);
  303.   },
  304.   onPageLoad : function(event){
  305.     var doc = event.originalTarget;
  306.     if (doc instanceof Components.interfaces.nsIDOMDocument) {
  307.       if (doc.defaultView.frameElement) {
  308.         // Frame within a tab was loaded. doc should be the root document of
  309.         // the frameset.
  310.         // Find the root document:
  311.         while (doc.defaultView.frameElement) {
  312.           doc=doc.defaultView.frameElement.ownerDocument;
  313.         }
  314.       }
  315.       var uri = null;
  316.       try{
  317.         uri = makeURI(doc.URL);
  318.       }catch(e){}
  319.       if (!uri) return;
  320.       this.onPreviewLoad(uri);
  321.     }
  322.   },
  323.   loadNextBookmark : function(){
  324.     var uri = null;
  325.     while (!uri && this.curPreview+1<this.allBookmarks.length){
  326.       this.curPreview++;
  327.       uri = this.allBookmarks[this.curPreview];
  328.       if (!uri || !(uri.scheme=="http" || uri.scheme=="https") ||
  329.           !PreviewService.shouldCreatePreview(uri) ||
  330.           PreviewService.bookmarks.hasPreview(uri.spec)){
  331.         uri = null;
  332.       }
  333.     }
  334.     var self = this;
  335.     if (!uri){
  336.       function generatetimedout(){
  337.         try{
  338.           if (self.status!="complete")
  339.             self.generatePreviewsComplete();
  340.         }catch(e){dump("ctimedoute: "+e+"\n");}
  341.       }
  342.       self.generateTimeout = setTimeout(generatetimedout,2000);
  343.     }
  344.     else{
  345.       var statusTextFld = document.getElementById("statusbar-display");
  346.       var statustext = this.bundle.GetStringFromName("creatingpreview.statustext");
  347.       statusTextFld.label = statustext.replace("$1",(this.curPreview+1)).replace("$2",this.allBookmarks.length).replace("$3",uri.spec)
  348.       var flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY;
  349.       try{
  350.         this.browser.loadURIWithFlags(uri.spec,flags,null,null);
  351.         function timedout(){
  352.           try{
  353.             self.generatePreview(uri);
  354.           }catch(e){dump("timedoute: "+e+"\n");}
  355.         }
  356.         self.generateTimeout = setTimeout(timedout,this.LOAD_TIMEOUT);
  357.       }catch(e){
  358.         dump("BPError: "+e);
  359.         this.loadNextBookmark();
  360.       }
  361.     }
  362.   },
  363.   generatePreviewsComplete : function(){
  364.     this.status = "complete";
  365.     alert(this.bundle.GetStringFromName("previewsgenerated"));
  366.     this.browser.removeProgressListener(this.previewGenListener);
  367.     this.browser.removeEventListener("load",this,true);
  368.     //this.browser = this.browser.parentNode.removeChild(this.browser);
  369.     this.browser.hidden = true;
  370.     this.previewGenListener = null;
  371.     this.allBookmarks = null;
  372.     this.browser = null;
  373.     this.curPreview = null;
  374.     this.bundle = null;
  375.   }
  376. }
  377. function BookmarkPreviewsProgressListener(){
  378.  
  379. }
  380. BookmarkPreviewsProgressListener.prototype = {
  381.   onURILoad : null,
  382.   getBrowser : null,
  383.   surpressDialogs : false,
  384.   QueryInterface: function(aIID) {
  385.    if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  386.        aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  387.        aIID.equals(Components.interfaces.nsISupports))
  388.      return this;
  389.    throw Components.results.NS_NOINTERFACE;
  390.   },
  391.   onStateChange: function(aProgress, aRequest, aFlag, aStatus) {
  392.     if (aFlag & STATE_START &&
  393.         aRequest && aRequest instanceof Ci.nsIChannel &&
  394.         this.surpressDialogs == true
  395.         ){
  396.       aRequest.notificationCallbacks = new BookmarkPreviewsListener();
  397.     }
  398.     if(aFlag & STATE_STOP){
  399.       if (aRequest instanceof Components.interfaces.nsIChannel){
  400.         var chan = aRequest.QueryInterface(Components.interfaces.nsIChannel);
  401.         if (!chan.URI.equals(chan.originalURI)){
  402.           var browser = this.getBrowser(chan.URI);
  403.           if (browser)
  404.             this.onURILoad(chan.originalURI,browser);
  405.         }
  406.       }
  407.     }
  408.     return 0;
  409.   },
  410.   onLocationChange: function(aProgress, aRequest, aURI){},
  411.   onProgressChange: function() {return 0;},
  412.   onStatusChange: function() {return 0;},
  413.   onSecurityChange: function() {return 0;},
  414.   onLinkIconAvailable: function() {return 0;}
  415. }
  416. /*
  417.  Surpress cert dialogs and other errors when loading sites in the background
  418. */
  419. function BookmarkPreviewsListener(callback){
  420.   this.callback = callback;
  421. }
  422. BookmarkPreviewsListener.prototype = {
  423.   /* Whether to load the site or not if there is an error */
  424.   load : false,
  425.   // nsIInterfaceRequestor
  426.   getInterface: function BKMKP_load_GI(aIID) {
  427.     return this.QueryInterface(aIID);
  428.   },
  429.   /* nsIBadCertListener2
  430.      return true to suppress the error message
  431.   */
  432.   notifyCertProblem: function BKMKP_certProblem(socketInfo, status, targetSite) {
  433.     dump("bad cert - notify cert problem\n");
  434.     return true;
  435.   },
  436.   /* nsIBadCertListener
  437.     return true to load the page, false to stop loading
  438.   */
  439.   confirmUnknownIssuer: function BKMKP_load_CUI(aSocketInfo, aCert,
  440.                                                aCertAddType) {
  441.     dump("bad cert issuer\n");
  442.     dump("cert url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  443.     return this.load;
  444.   },
  445.   confirmMismatchDomain: function BKMKP_load_CMD(aSocketInfo, aTargetURL,
  446.                                                 aCert) {
  447.     dump("bad cert - mismatch domain\n");
  448.     dump("cert url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  449.     return this.load;
  450.   },
  451.   confirmCertExpired: function BKMKP_load_CCE(aSocketInfo, aCert) {
  452.     dump("notifyCertProblem - certexpired\n");
  453.     dump("cert url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  454.     return this.load;
  455.   },
  456.   notifyCrlNextupdate: function BKMKP_load_NCN(aSocketInfo, aTargetURL, aCert) {
  457.     dump("bad cert - crlnextupdate\n");
  458.     dump("cert url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  459.   },
  460.   /* nsISSLErrorListener
  461.     return true to suppress the error
  462.   */
  463.   notifySSLError: function BKMKP_SSLError(socketInfo, error, targetSite) {
  464.     dump("ssl error\n")
  465.     dump("site url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  466.     return true;
  467.   },
  468.   /* nsIAuthPrompt
  469.     return false to cancel
  470.   */
  471.   prompt : function ( dialogTitle,  text,  passwordRealm,  savePassword,  defaultText,  result){
  472.     dump("prompt site url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  473.     return false;
  474.   },
  475.   // nsIAuthPrompt
  476.   promptUsernameAndPassword : function( dialogTitle,  text,  passwordRealm,  savePassword,  user,  pwd){
  477.     dump("promptUAP site url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  478.     return false;
  479.   },
  480.   // nsIAuthPrompt
  481.   promptPassword : function( dialogTitle,  text,  passwordRealm,  savePassword, pwd){
  482.     dump("promptP site url: "+(PreviewService.generator.allBookmarks[PreviewService.generator.curPreview].spec)+"\n");
  483.     return false;
  484.   },
  485.   QueryInterface: function BKMKP_loadQI(aIID) {
  486.     if (aIID.equals(Ci.nsISupports)           ||
  487.         aIID.equals(Ci.nsIInterfaceRequestor) ||
  488.         aIID.equals(Ci.nsIBadCertListener)    || //FF2
  489.         aIID.equals(Ci.nsIBadCertListener2)   || //FF3
  490.         aIID.equals(Ci.nsISSLErrorListener)   ||
  491.         aIID.equals(Ci.nsIAuthPrompt)         ||
  492.         false){
  493.       return this;
  494.     }
  495.     throw Cr.NS_ERROR_NO_INTERFACE;
  496.   }
  497. }
  498. function PreviewServiceFF3(){
  499.   this.dir = Components.classes["@mozilla.org/file/directory_service;1"]
  500.                      .getService(Components.interfaces.nsIProperties)
  501.                      .get("ProfD", Components.interfaces.nsIFile);
  502.   this.dir.append("bookmarkpreviews");
  503.   if (!this.dir.exists())
  504.     this.dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,0777);
  505.  
  506.   this.register();
  507. }
  508. PreviewServiceFF3.prototype = {
  509.   toString : function(){return "Firefox 3 Preview Service";},
  510.   txnlistener : null,
  511.   dir : null,
  512.   unload : function(){
  513.     this.unregister();
  514.   },
  515.   register : function(){
  516.     PlacesUtils.bookmarks.addObserver(this.observer, false);
  517.   },
  518.   unregister : function(){
  519.     PlacesUtils.bookmarks.removeObserver(this.observer,false);
  520.   },
  521.   isBookmarked : function(uri){
  522.     return PlacesUtils.bookmarks.getBookmarkedURIFor(uri)!=null;
  523.   },
  524.   hasPreview : function(aUrl){
  525.     return PreviewServiceUtils.getPreviewFile(aUrl,this.dir).exists();
  526.   },
  527.   savePreview : function(data,uri){
  528.     var file = PreviewServiceUtils.getPreviewFile(uri.spec,this.dir);
  529.     var io = Components.classes["@mozilla.org/network/io-service;1"]
  530.                        .getService(Components.interfaces.nsIIOService);
  531.     var source = io.newURI(data, "UTF8", null);
  532.     var target = io.newFileURI(file)
  533.     // prepare to save the data
  534.     var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
  535.                             .createInstance(Components.interfaces.nsIWebBrowserPersist);
  536.     persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  537.     persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  538.  
  539.     persist.saveURI(source, null, null, null, null, file);
  540.   },
  541.   /* generate all previews... */
  542.   getAllBookmarks : function(root,bkmks){
  543.     if (!root){
  544.       var roots = Application.bookmarks;
  545.       root = roots.children?roots:{children: [roots.menu,roots.toolbar,roots.unfiled]};
  546.       bkmks = [];
  547.     }
  548.     var ls = Cc["@mozilla.org/browser/livemark-service;2"]
  549.                         .getService(Ci.nsILivemarkService);
  550.     root.children.forEach(function(item,idx){
  551.       if (item.type=="bookmark"){
  552.         bkmks.push(item.uri);
  553.       }
  554.       else if (item.type=="folder" && !ls.isLivemark(item.id)){
  555.         this.getAllBookmarks(item,bkmks);
  556.       }
  557.     },this);
  558.     return bkmks;
  559.   },
  560.  
  561.   observer : {
  562.     get mOuter(){return PreviewService.bookmarks;},
  563.     changed: {id: null, uri: null},
  564.     /* nsINavBookmarkObserver */
  565.     onBeginUpdateBatch : function(){},
  566.     onEndUpdateBatch : function(){},
  567.     onItemAdded : function(aItemId, aFolder, aIndex){
  568.       var uri = PlacesUtils.bookmarks.getBookmarkURI(aItemId);
  569.       PreviewService.bookmarkAdded(uri);
  570.     },
  571.     /*
  572.      Since this is method is called after a bookmark is already removed we can't
  573.      get the bookmark's url. To work around this we record the uri in the itemchange
  574.      method which is called before the bookmark is removed
  575.     */
  576.     onItemRemoved : function(aItemId,  aFolder,  aIndex){
  577.       if (aItemId == this.changed.id){
  578.         var uri = this.changed.uri;
  579.         if (!this.mOuter.isBookmarked(uri)){
  580.           var file = PreviewServiceUtils.getPreviewFile(uri.spec,this.mOuter.dir);
  581.           if (file.exists())
  582.             file.remove(false);
  583.         }
  584.       }
  585.     },
  586.     onItemChanged : function(aBookmarkId, aProperty, aIsAnnotationProperty, aValue){
  587.       //dump("changed bookmark - "+aBookmarkId+" - prop: "+aProperty+" ann: "+aIsAnnotationProperty+" - value: "+aValue+"\n");
  588.       if (["title","favicon","tags","cleartime","keyword","bookmarkProperties/description",
  589.            "bookmarkProperties/loadInSidebar"].indexOf(aProperty)>-1)
  590.         return;
  591.       else if (aProperty == "uri"){
  592.         //remove old... add new
  593.         //but we can't remove the old since it's already been removed
  594.         //aValue == new url and getting the bookmarked uri == the new url
  595.         try{/* catch malformed uri error */
  596.           PreviewService.bookmarkAdded(makeURI(aValue));
  597.         }catch(e){}
  598.       }
  599.       else{
  600.         this.changed.id = aBookmarkId;
  601.         this.changed.uri = PlacesUtils.bookmarks.getBookmarkURI(aBookmarkId);
  602.       }
  603.     },
  604.     onItemVisited : function(aBookmarkId,  aVisitID, time){},
  605.     onItemMoved : function(aItemId,  aOldParent,  aOldIndex,  aNewParent,  aNewIndex){}
  606.   }
  607. }
  608. function PreviewServiceFF2(){
  609.   initServices();
  610.   initBMService();
  611.   this.bmsvc = BMSVC;
  612.   this.dir = Components.classes["@mozilla.org/file/directory_service;1"]
  613.                      .getService(Components.interfaces.nsIProperties)
  614.                      .get("ProfD", Components.interfaces.nsIFile);
  615.   this.dir.append("bookmarkpreviews");
  616.   if (!this.dir.exists())
  617.     this.dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,0777);
  618.  
  619.   this.register();
  620. }
  621. PreviewServiceFF2.prototype = {
  622.   txnlistener : null,
  623.   toString : function(){return "Firefox 3 Preview Service";},
  624.   unload : function(){
  625.     this.unregister();
  626.   },
  627.   register : function(){
  628.     var bkmkTxnSvc = Components.classes["@mozilla.org/bookmarks/transactionmanager;1"]
  629.             .getService(Components.interfaces.nsIBookmarkTransactionManager);
  630.     this.txnlistener = new BookmarkPreviewsTransactionListener(this);
  631.     bkmkTxnSvc.transactionManager.AddListener(this.txnlistener);
  632.   },
  633.   unregister : function(){
  634.     var bkmkTxnSvc = Components.classes["@mozilla.org/bookmarks/transactionmanager;1"]
  635.             .getService(Components.interfaces.nsIBookmarkTransactionManager);
  636.     bkmkTxnSvc.transactionManager.RemoveListener(this.txnlistener);
  637.   },
  638.   isBookmarked : function(uri){
  639.     var NC = "http://home.netscape.com/NC-rdf#";
  640.     var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
  641.        var BMDS  = RDF.GetDataSource("rdf:bookmarks");
  642.     var urlArc = RDF.GetResource(NC+"URL");
  643.     var elements = BMDS.GetAllResources();
  644.     while (elements.hasMoreElements()){
  645.       var ele = elements.getNext();
  646.       if(BMDS.hasArcOut(ele, urlArc)){
  647.         var target = BMDS.GetTarget(ele, urlArc, true);
  648.         var value = target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  649.         if(value == uri.spec){
  650.           ele.QueryInterface(Components.interfaces.nsIRDFResource);
  651.           var resParent= BMSVC.getParent(ele);
  652.           if (resParent){
  653.             var name = BookmarksUtils.getProperty(resParent, "http://home.netscape.com/NC-rdf#Name");
  654.             if (name!=""){
  655.               return true;
  656.             }
  657.           }
  658.         }
  659.       }
  660.     }
  661.     return false;
  662.   },
  663.   hasPreview : function(aUrl){
  664.     return PreviewServiceUtils.getPreviewFile(aUrl,this.dir).exists();
  665.   },
  666.   savePreview : function(data,uri,aArg){
  667.     var file = PreviewServiceUtils.getPreviewFile(uri.spec,this.dir);
  668.     var io = Components.classes["@mozilla.org/network/io-service;1"]
  669.                        .getService(Components.interfaces.nsIIOService);
  670.     var source = io.newURI(data, "UTF8", null);
  671.     var target = io.newFileURI(file);
  672.     if (file.exists())
  673.       file.remove(true);
  674.     // prepare to save the data
  675.     var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
  676.                             .createInstance(Components.interfaces.nsIWebBrowserPersist);
  677.     persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  678.     persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  679.  
  680.     persist.saveURI(source, null, null, null, null, file);
  681.   },
  682.   removePreview : function(url){
  683.     try{/* catch malformed uri and file io errors */
  684.       if (!this.isBookmarked(makeURI(url))){
  685.         var file = PreviewServiceUtils.getPreviewFile(url,this.dir);
  686.         if (file.exists())
  687.           file.remove(false);
  688.       }
  689.     }catch(e){}
  690.   },
  691.   observeTransaction : function(aState, aTxn, aDo){
  692.     if (!aTxn) return;
  693.     aTxn = aTxn.wrappedJSObject;
  694.     //dump("observe transaction: "+aTxn.type+" action: "+aTxn.action+"\n");
  695.     if ((aTxn.type!="insert" && aTxn.type!="remove") || aTxn.action == "move") return;
  696.     var type = aTxn.type;
  697.     var item = aTxn.item;
  698.     var isContainer = null;
  699.     function resourceIsContainer(aResource){
  700.       var type = BookmarksUtils.resolveType(aResource);
  701.       return type.indexOf("Folder")>-1 ||
  702.              type == "Livemark"        ||
  703.              false;
  704.     }
  705.     /*
  706.      When removing a bookmark folder the type is originally 'Folder' in the
  707.      'will' stage, but after it is removed the type is 'ImmutableBookmark'. We
  708.      need to check what type it is before the item is actually removed.
  709.     */
  710.     if (aTxn.type=="remove" && aDo){
  711.       if (aState == "will"){
  712.         this._transactionItemType = resourceIsContainer(item);
  713.         return;
  714.       } else {
  715.         isContainer = this._transactionItemType;
  716.         delete this._transactionItemType;
  717.       }
  718.     } else
  719.       isContainer = resourceIsContainer(item);//RDFCU.IsContainer(BMDS, RDF.GetResource(item));
  720.     if (aState=="will") return;
  721.  
  722.     var urls = [];
  723.     if (isContainer){
  724.       try{
  725.         var propArray = [];
  726.         BookmarksUtils.getAllChildren(item, propArray);
  727.         propArray.forEach(function(props,idx1){
  728.           var prop = props[1];
  729.           if (prop)
  730.             urls.push(prop.Value);
  731.         });
  732.       }
  733.       catch(e){}
  734.     }
  735.     var itemURL = null;
  736.     itemURL = aTxn.removedProp[1];
  737.     if (itemURL)
  738.       urls.push(itemURL.Value);
  739.     else{
  740.       itemURL = BookmarksUtils.getProperty(aTxn.item,aTxn.Properties[1].Value);
  741.       if (itemURL)
  742.         urls.push(itemURL);
  743.     }
  744.     urls.forEach(function(url,idx){
  745.       try{/* catch malformed uri errors */
  746.         if (((type == "insert" && aDo) || (type == "remove" && !aDo)) && url){
  747.           PreviewService.bookmarkAdded(makeURI(url));
  748.         }
  749.         else if (((type == "remove" && aDo) || (type == "insert" && !aDo)) && url){
  750.           this.removePreview(url);
  751.         }
  752.       }catch(e){}
  753.     },this);
  754.   },
  755.   /* Used for generating all previews */
  756.   getAllBookmarks : function(){
  757.     var children = [], all = [];
  758.     BookmarksUtils.getAllChildren(RDF.GetResource("NC:BookmarksRoot"),children);
  759.     for (var i = 0; i < children.length; i++){
  760.       try{
  761.         if (children[i][1] && children[i][1].Value!=null){
  762.           all.push(makeURI(children[i][1].Value));
  763.         }
  764.       }catch(e){dump(e+"\n");}
  765.     }
  766.     return all;
  767.   }
  768. }
  769. function BookmarkPreviewsTransactionListener(parent){
  770.   this.parent = parent;
  771. }
  772. BookmarkPreviewsTransactionListener.prototype = {
  773.   willDo: function (aTxmgr, aTxn) {
  774.     this.parent.observeTransaction("will", aTxn, true);
  775.   },
  776.   didDo : function (aTxmgr, aTxn) {
  777.     this.parent.observeTransaction("did", aTxn, true);
  778.   },
  779.   willUndo: function (aTxmgr, aTxn) {
  780.     this.parent.observeTransaction("will", aTxn, true);
  781.   },
  782.   didUndo : function (aTxmgr, aTxn) {
  783.     this.parent.observeTransaction("did", aTxn, false);
  784.   },
  785.   willRedo: function (aTxmgr, aTxn) {
  786.     this.parent.observeTransaction("will", aTxn, true);
  787.   },
  788.   didRedo : function (aTxmgr, aTxn) {
  789.     this.parent.observeTransaction("did", aTxn, true);
  790.   },
  791.   didMerge       : function (aTxmgr, aTxn) {},
  792.   didBeginBatch  : function (aTxmgr, aTxn) {},
  793.   didEndBatch    : function (aTxmgr, aTxn) {
  794.     this.parent.observeTransaction("did", aTxn, true);
  795.   },
  796.   willMerge      : function (aTxmgr, aTxn) {},
  797.   willBeginBatch : function (aTxmgr, aTxn) {},
  798.   willEndBatch   : function (aTxmgr, aTxn) {}
  799. }
  800. window.addEventListener('load',function(e) { PreviewService.onLoad(e); },false);
  801. window.addEventListener('unload',function(e) { PreviewService.onUnload(e); },false);
  802.